#pragma once
#include "sock.hpp"
#include "log.hpp"
#include <functional>

uint16_t default_port = 8080;
static const int fd_num = sizeof(fd_set) * 8; // 获取fd结构体的大小
static const int default_fd = -1;             // 默认文件描述符
using func_t = function<string(const string &)>;

class SelectServer
{
public:
    SelectServer(func_t func, uint16_t port = default_port)
        : _listensock(-1), _port(port), _fdarray(nullptr), _func(func)
    {
    }
    void Print()
    {
        for (int i = 0; i < fd_num; ++i)
        {
            if (_fdarray[i] != default_fd)
            {
                cout << _fdarray[i] << " ";
            }
        }
        cout << endl;
    }
    // 这里listensock触发的情况
    void Accepter()
    {
        logMessage(NORMAL,"Accepter in");
        // 这里select一定已经帮我们完成的等待的工作，所以直接获取新连接会使效率大大提高
        string clientip;
        uint16_t clientport;
        int sock = sock::Accept(_listensock, &clientip, &clientport);
        if (sock < 0)
            return;
        logMessage(NORMAL, "accept success: [%s:%d]", clientip.c_str(), clientport);
        // 把新的sock放入到fdarray中，让其帮我们进行等待工作
        int i = 0;
        for (; i < fd_num; ++i)
        {
            if (_fdarray[i] == default_fd)
                break;
        }
        // 这里有可能已经超过了最大的fdarray的范围
        if (_fdarray[i] == fd_num)
        {
            logMessage(WARNING, "server is full,please wait");
            close(sock);
        }
        else
            _fdarray[i] = sock;
        // 打印出数组中的有效的fd
        Print();
        logMessage(NORMAL,"Accepter out");
    }
    void Recver(int sock, int pos)
    {
        logMessage(NORMAL,"Recver in");
        // 这里select已经帮我们完成等待工作，下面直接读取即可，然后处理返回
        // 1.接受request
        char buffer[1024];
        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (s > 0)
        {
            buffer[s] = 0;
            logMessage(NORMAL, "client say:%s", buffer);
        }
        else if (s == 0)
        {
            // 读完，关闭fd，同时置空当前数组的fd
            close(sock);
            _fdarray[pos] = default_fd;
            logMessage(NORMAL, "client quit");
            return;
        }
        else
        {
            // 读取错误，关闭fd，并置空fd
            close(sock);
            _fdarray[pos] = default_fd;
            logMessage(ERROR, "client quit %s", strerror(errno));
            return;
        }
        // 通过回调函数，处理业务逻辑，这里只是简单的测试
        // 2.处理request
        string response = _func(buffer);
        // 3.返回response，这里本来应该增加一个对write端写的select控制，但是这样太复杂了，只验证读即可
        write(sock, response.c_str(), response.size());
        logMessage(NORMAL, "Recver out");
    }
    void handlerEvent(fd_set &rfds)
    {
        // 因为我们是需要遍历所有的fd的，我们并不知道哪个fd已经就绪，所以这里要全部遍历一次
        for (int i = 0; i < fd_num; ++i)
        {
            // 首先先排除一些无效的fd
            if (_fdarray[i] == default_fd)
                continue;
            // 这里是listensock的情况
            if (FD_ISSET(_listensock, &rfds) && _listensock == _fdarray[i])
            {
                Accepter();
            }
            else if (FD_ISSET(_fdarray[i], &rfds))
            {
                // 其他的读情况条件满足
                Recver(_fdarray[i], i);
            }
            else
            {
            }
        }
    }
    void initServer()
    {
        // 创建套接字
        _listensock = sock::Socket();
        // 绑定
        sock::Bind(_listensock, _port);
        // 监听
        sock::Listen(_listensock);
        // 对fdarray的初始化
        _fdarray = new int[fd_num];
        for (int i = 0; i < fd_num; ++i)
        {
            _fdarray[i] = default_fd;
        }
        // 这里因为已经创建好监听套接字，可以直接加入fdarray中
        _fdarray[0] = _listensock;
    }
    void start()
    {
        for (;;)
        {
            fd_set rfds;
            FD_ZERO(&rfds);
            // 更新最大
            int maxfd = _listensock;
            // 把fdarray中的已经有的套接字放入fdarray中
            for (int i = 0; i < fd_num; ++i)
            {
                if (_fdarray[i] == default_fd)
                    continue;
                // 设置进位图
                FD_SET(_fdarray[i], &rfds);
                // 更新最大的fd
                if (_fdarray[i] > maxfd)
                    maxfd = _fdarray[i];
            }
            logMessage(NORMAL,"maxfd:%d",maxfd);
            // 这里可以使用select来帮我们进行等待检测
            int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
            switch (n)
            {
            case 0:
            {
                // 这里说明是等待时间到了
                logMessage(NORMAL, "timeout ...");
                break;
            }
            case -1:
            {
                // 这里是出错了,打印错误码
                logMessage(WARNING, "select error code:%d,err string:%s", errno, strerror(errno));
                break;
            }
            default:
            {
                // 这里说明已经有至少一个链接就绪了，我们可以进行读取了
                logMessage(NORMAL, "have a event ready!");
                handlerEvent(rfds);
                break;
            }
            }
        }
    }
    ~SelectServer()
    {
        if (_listensock > 0)
        {
            close(_listensock);
        }
        delete[] _fdarray;
    }

private:
    int _listensock; // 监听套接字
    uint16_t _port;  // 端口号
    int *_fdarray;   // 用来对文件描述符的管理
    func_t _func;
};
